home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / music / ptmid.arj / PTMIDZAP.C < prev    next >
C/C++ Source or Header  |  1994-01-08  |  8KB  |  211 lines

  1. /*
  2.  * ptmidzap.c: Resolution module for ptmid. Takes a structure representing
  3.  * a tune and tries to make it fit into 4 channels.
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "ptmid.h"
  14.  
  15. MS rgmsDecided[MAXPTSAMPS];
  16. int cmsDecided = 0, wVolfract = 1;
  17.  
  18. /**
  19.  ** The midiperiod1 array allows quick conversion between MIDI note
  20.  ** values and Protracker note values. The midiperiod2 array is
  21.  ** similar, but uses an extended range of values. All "out-of-bounds"
  22.  ** values are given closest legal value.
  23.  **/
  24. unsigned midiperiod1[128] = {
  25.     856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856,
  26.     856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856,
  27.     856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856,
  28.     856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856,
  29.     856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  30.     428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  31.     214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
  32.     113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
  33.     113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
  34.     113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
  35.     113, 113, 113, 113, 113, 113, 113, 113
  36.     };
  37. unsigned midiperiod2[128] = {
  38.     1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  39.     1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  40.     1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  41.     1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961, 907,
  42.     856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  43.     428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  44.     214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
  45.     107, 101, 95,  90,  85,  80,  76,  71,  67,  64,  60,  57,
  46.     57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  47.     57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  48.     57,  57,  57,  57,  57,  57,  57,  57
  49.     };
  50.  
  51. /**
  52.  ** The midivolume array does the same thing as the midiperiod array
  53.  ** except it is for velocity-volume conversion.
  54.  **/
  55. unsigned midivolume[128] = {
  56.      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  57.     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  58.     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  59.     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  60.     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  61.     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  62.     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  63.     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
  64.   };
  65.  
  66. /*
  67.  * Init: Initializes stuff that should be initialized.
  68.  */
  69. static void Init()
  70. {
  71.     int ims = MAXPTSAMPS;
  72.  
  73.     while (ims--) { /** Put default values in sample array **/
  74.         sprintf(rgmsDecided[ims].szName, "%02d: --", ims);
  75.         rgmsDecided[ims].cMix = 0;
  76.         rgmsDecided[ims].bDefvol = 0;
  77.     }
  78. }
  79.  
  80. /*
  81.  * FitSzBFn: Given a filename, will fit it into the given sample name,
  82.  * and prepend given number, ensuring that it doesn't overflow the
  83.  * 22 character array.
  84.  */
  85. void FitSzBFn(Sz szName, int bPos, Sz fnSample)
  86. {
  87.     int iT = 18;
  88.  
  89.     sprintf(szName, "%02d: ", bPos); /** First the number **/
  90.     szName += 4;
  91.     while (iT-- && (*(szName++) = *(fnSample++))); /** Then the name **/
  92. }
  93.  
  94. /*
  95.  * AnalyzePtune: Given a tune, searches through and allocates samples
  96.  * to sample array rgmsDecided (includes determining mixes).
  97.  * Eventually (?) this will become much more extensive, and include
  98.  * compacting by changing tempo, filtering of quiet notes, and identification
  99.  * of common chords and replacing these with a separate sample.
  100.  */
  101. void AnalyzePtune(Tune *ptune)
  102. {
  103.     unsigned long Lastnoise = 0;
  104.     EI *pei;
  105.     SI *psi, **ppsi;
  106.     NI *pni;
  107.     int ini;
  108.  
  109.     while (NULL != ptune) { /** While not at end of tune **/
  110.         for (pei = ptune->pei; NULL != pei; pei = pei->peiNext) /** With each event **/
  111.             if (0 != pei->cni) {
  112.                 if (ptune->count + pei->effect > Lastnoise) /** Find the note which **/
  113.                     Lastnoise = ptune->count + pei->effect; /** will carry the longest **/
  114.  
  115.                 for (ini = pei->cni; ini--; ) { /** For each note in event **/
  116.                     pni = pei->pni;
  117.                     if (pni[ini].inst < 0) /** If a percussion instrument **/
  118.                         if ((psi = rgpsiDrum[-1 - pni[ini].inst]) == NULL)
  119.                             pni[ini].inst = -1;
  120.                         else if (fExtend) /** convert pitch to protracker period **/
  121.                             pni[ini].pitch = midiperiod2[psi->pitch];
  122.                         else
  123.                             pni[ini].pitch = midiperiod1[psi->pitch];
  124.                     else { /** Else if a non-percussion instrument **/
  125.                         unsigned wMin;
  126.  
  127.                         if ((ppsi = rgppsiIns[pni[ini].inst]) == NULL)
  128.                             ppsi = rgppsiIns[128];
  129.                         psi = *(ppsi++);
  130.                         wMin = abs(psi->pitch - pni[ini].pitch);
  131.                         for (; NULL != *ppsi; ppsi++) /** Find closest matching sample **/
  132.                             if (abs((*ppsi)->pitch - pni[ini].pitch) < wMin) {
  133.                                 psi = *ppsi;
  134.                                 wMin = abs(psi->pitch - pni[ini].pitch);
  135.                             }
  136.                         wMin = 60 + pni[ini].pitch - psi->pitch;
  137.                         if (fExtend) /** And use it's base note when converting to **/
  138.                             pni[ini].pitch = midiperiod2[wMin]; /** protracker periods **/
  139.                         else
  140.                             pni[ini].pitch = midiperiod1[wMin];
  141.                     }
  142.  
  143.                     if (NULL != psi) /** If sample info **/
  144.                         if (-1 != psi->sample) /** and sample has been used before **/
  145.                             pni[ini].inst = psi->sample; /** use that sample for instrument **/
  146.                         else if (MAXPTSAMPS == cmsDecided) /** Else if no samples left **/
  147.                             pni[ini].inst = -1; /** make note invalid **/
  148.  
  149.                         else { /** Else we've got a new sample **/
  150.                             FitSzBFn(rgmsDecided[cmsDecided].szName, cmsDecided,
  151.                                 psi->fnSample); /** Fix up it's name **/
  152.                             rgmsDecided[cmsDecided].bDefvol = 64;
  153.                             rgmsDecided[cmsDecided].cMix = 1;
  154.                             if ((ppsi = (SI **) malloc(sizeof(SI *))) == NULL) {
  155.                                 fprintf(stderr, "ptmid: Cannot allocate any more memory\n");
  156.                                 exit(1);
  157.                             } /** Give it a spot in memory **/
  158.                             *ppsi = psi;
  159.                             rgmsDecided[cmsDecided].ppsiMix = ppsi; /** Put it in array **/
  160.                             rgmsDecided[cmsDecided].pwMixnote = NULL;
  161.                             psi->sample = pni[ini].inst = cmsDecided++;
  162.                         }
  163.                 }
  164.             }
  165.         ptune = ptune->ptune;
  166.     }
  167. }
  168.  
  169. /*
  170.  * UnitifyPtune: Given a tune, will convert its volume into Protracker
  171.  * standard units, as well as convert lengths into division multiples.
  172.  */
  173. void UnitifyPtune(Tune *ptune)
  174. {
  175.     EI *pei;
  176.     NI *pni;
  177.     int cni;
  178.  
  179.     while (NULL != ptune) { /** While not end of tune **/
  180.         pei = ptune->pei;
  181.         ptune->count /= wQuant; /** Divide intervals by quantize amount **/
  182.         ptune = ptune->ptune;
  183.         while (NULL != pei) { /** Go through each event **/
  184.             if (pei->cni) {
  185.                 pei->effect /= wQuant; /** Divide durations by quantize amount **/
  186.                 /**
  187.                  ** When implemented, samples that are chords will have volumes
  188.                  ** smaller than other samples (ie. 3 notes in one sample has
  189.                  ** 1/3 volume for each note), so much divide all other volumes
  190.                  ** to even things out.
  191.                  **/
  192.                 for (cni = pei->cni, pni = pei->pni; cni--; pni++)
  193.                     pni->vol = midivolume[pni->vol] / wVolfract;
  194.             }
  195.             pei = pei->peiNext;
  196.         }
  197.     }
  198. }
  199.  
  200. /*
  201.  * ResolvePtune: Given a tune, goes through and finds out useful stuff
  202.  * about it for use in creating a MOD-file. Also trims off chords that
  203.  * are too big, notes that are too quiet, etc.
  204.  */
  205. void ResolvePtune(Tune *ptune)
  206. {
  207.     Init();
  208.     AnalyzePtune(ptune);
  209.     UnitifyPtune(ptune);
  210. }
  211.